#include<assert.h>
#include<iostream>
#include<string>
#include<stdio.h>
#include<unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define BACKLOG 1024

//封装一个 TCP 接口
class TcpSocket{
  private:
    int _sockfd;
  public:
    TcpSocket():_sockfd(-1) {}
    ~TcpSocket(){
        /*Close();
        _sockfd=-1;*/
    }

    //获取套接字描述符信息
    int fd(){
      return _sockfd;
    }
    void set(int fd){
      _sockfd=fd;
    }

    //创建套接字:TCP 协议
    bool Socket()
    {
      _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
      if(_sockfd<0){
        perror("socket error!\n");
        return false;
      }
      return true;
    }
    //绑定端口
    bool Bind(const std::string &ip,uint16_t port)
    {
      struct sockaddr_in addr;  //ipv4 地址结构
      addr.sin_family=AF_INET;  //ipv4 地址域
      addr.sin_port=htons(port);   //将主机套接字转换为网络套接字
      addr.sin_addr.s_addr=inet_addr(ip.c_str());  //将地址信息转化为整型 IP 地址
      socklen_t len=sizeof(struct sockaddr_in);

      int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
      if(ret<0){
        perror("bind error!\n");
        return false;
      }
      return true;
    }

    //开始监听
    bool Listen(int backlog = BACKLOG)
    {
      int ret=listen(_sockfd,backlog);
      if(ret<0){
        perror("listen error!\n");
        return false;
      }
      return true;
    }

    //建立连接
    bool Connect(const std::string& srvip,uint16_t srvport)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(srvport); //主机字节序转换为网络字节序
      addr.sin_addr.s_addr=inet_addr(srvip.c_str());  //将IP转换为整型IP
      socklen_t len=sizeof(struct sockaddr_in);

      int ret=connect(_sockfd,(struct sockaddr*)&addr,len);
      if(ret<0){
        perror("connect error!\n");
        return false;
      }
      return true;
    }

    //接收连接，获取新建连接的客户端地址信息
    bool Accept(TcpSocket* new_sock,std::string* cli_ip=NULL,uint16_t* cli_port=NULL)
    {
      struct sockaddr_in addr;
      socklen_t len=sizeof(struct sockaddr_in);
      int newfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
      if(newfd<0){
        perror("accept error!\n");
        return false;
      }
      new_sock->_sockfd=newfd;   //尽力新连接的套接字描述符
      if(cli_ip != NULL)
        *cli_ip=inet_ntoa(addr.sin_addr); //将获取到的客户端 IP 地址转换为点分十进制
      if(cli_port!=NULL)
        *cli_port=ntohs(addr.sin_port);//将客户端端口从网络套接字 IP 转换为 主机套接字（因为 ipv4 地址域类型为 uint16 含有两字节因此使用 ntohs）
      return true;
    }

    //发送数据
    bool Send(const std::string& body)
    {
      ssize_t ret=send(_sockfd,body.c_str(),body.size(),0);  //0-阻塞等待
      if(ret<0){
        perror("send error!\n");
        return false;
      }
      return true;
    }


    //接收数据
    bool Recv(std::string* body)
    {
      char tmp[1024]={0};
      //recv 返回值大于 0；等于 0（表示断开连接）；小于0（表示出错）
      ssize_t ret =recv(_sockfd,tmp,1023,0);  //0-阻塞等待
      if(ret<0){
        perror("recv error!\n");
        return false;
      }
      else if(ret==0){  //接收数据返回值为 0 表示连接断开
        printf("connect shutdown!\n");
        return false;
      }
      body->assign(tmp,ret);  //从 tmp 截取 ret 长度数据放到 body 
      return true;
    }
    
    //关闭套接字
    bool Close()
    {
      if(_sockfd!=-1){
        close(_sockfd);
        _sockfd=-1;
      }
      return true;
    }
};



